using System;
using System.Collections.Generic;
using UI;
using UnityEngine;

namespace AutoChess
{
    /*
     * 状态基类:
     */
    class SMBaseState
    {
        public static int NullTransitonID = 0;
        public static int NullStateId = 0;
        
        protected Dictionary<int,int> map = new Dictionary<int, int>();
        protected int stateID;

        public int ID
        {
            get { return stateID; }
            set { stateID = value; }
        }

        public bool IsRunning
        {
            get { return StateMachine.GetCurrentStateID() == stateID; }
        }

        public bool m_isDestroy = false;
        public int m_lastStateID;
        
        /// <summary>
        /// Reference to state machine
        /// </summary>
        public XStateMachine StateMachine { get; set; }

        protected GameActor OwnActor
        {
            get
            {
                if (StateMachine == null)
                    return null;
                else
                    return StateMachine.OwnActor;
            }
        }

        public void AddTransition(int trans, int id)
        {
            //Check if anyone of the args is invalid
            if(trans ==NullTransitonID || id==NullStateId)
                return;
            //since this is a Deterministic FSM, check if the current transition was already inside the map
            if(map.ContainsKey(trans))
                return;
            map.Add(trans,id);
        }

        public void DeleTransition(int trans)
        {
            if(trans == NullStateId)
                return;
            if (map.ContainsKey(trans))
            {
                map.Remove(trans);
                return;
            }
        }

        public int GetOutputState(int trans)
        {
            if (map.ContainsKey(trans))
            {
                return map[trans];
            }

            return NullStateId;
        }

        public virtual void InitializedState()
        {
        }

        public virtual void OnEnter(object data = null)
        {
        }

        public virtual void OnLeave()
        {

        }

        /// <summary>
        /// This method decides if the state should transition to another on its list
        /// NPC is a reference to the object that is controlled by this class
        /// </summary>
        public virtual void OnReason(int transID, object data = null)
        {
            bool shouldChange = this.GetOutputState(transID) != NullStateId;
            if (shouldChange)
            {
                this.StateMachine.PerformTransition(transID, data);
            }
        }

        /// <summary>
        /// This method controls the behavior of the NPC in the game World.
        /// Every action ,movement or communication the NPC does should be placed here 
        /// NPC is a reference to the object that is controlled by this class
        /// </summary>
        public virtual void OnUpdate()
        {
        }

        /// <summary>
        /// this method is used to clean. when statemachine is destroyed，
        /// all its state will be informed by calling this methode. Do cleaning something here;
        /// </summary>
        public virtual void OnClean()
        {
        }

        public virtual string OnGetDebugInfomation()
        {
            return null;
        }
    }

    /// <summary>
    /// It has a list with the States the NPC has and methods to add,
    /// delete a state,and to change the current state the Machine is on.
    /// </summary>
    class XStateMachine:ActorCmpt
    {
        protected List<SMBaseState> states;

        public int _currentStateID;
        public int _oldStateID;

        public SMBaseState _currentState;
        
        public GameActor OwnActor { get; set; }
        
        public int GetCurrentStateID()
        {
            return _currentStateID;
        }

        protected void Awake()
        {
            states = new List<SMBaseState>();

            OnStart();
            initiallizeStateMachine();
            
            _currentState.OnEnter();
        }

        protected virtual void OnStart()
        {
        }

        protected virtual void initiallizeStateMachine()
        {
        }

        protected void AddState(SMBaseState state)
        {
            if (state == null)
                return;

            if (states.Count == 0)
            {
                states.Add(state);
                _currentStateID = state.ID;
                _currentState = state;
                return;
            }

            foreach (var s in states)
            {
                if(s.ID == state.ID)
                    return;
            }
            states.Add(state);
        }
       
        public void PerformTransition(int transID, object data)
        {
            if (transID == SMBaseState.NullTransitonID)
            {
                return;
            }

            var id = _currentState.GetOutputState(transID);
            if(id == SMBaseState.NullStateId)
                return;

            SMBaseState preState = _currentState;
            SMBaseState newState = null;
            foreach (var state in states)
            {
                if (state.ID == id)
                {
                    newState = state;
                    break;
                }
            }

            if (newState == null)
            {
                BLogger.Error("invalid state id:{0}",id);
            }
            
            BLogger.Assert(newState!=null);
            
            preState.OnLeave();

            _oldStateID = _currentStateID;
            _currentState = newState;
            _currentStateID = id;
            
            _currentState.OnEnter(data);
            
            OnStateChange();

            if (_currentState.ID != id)
            {
                BLogger.Error("set state failed!");
            }
        }

        protected virtual void OnStateChange()
        {
        }

        protected virtual void OnDestroy()
        {
            if (_currentState != null)
            {
                _currentState.m_isDestroy = true;
                _currentState.OnLeave();
                _currentState = null;
                _currentStateID = 0;
            }

            if (this.states != null)
            {
                this.states.ForEach(st => { st.OnClean();});
            }
        }

        public virtual void OnNextTargetLocked(GameObject nextTarget)
        {
        }

    }
}